import CanvasTools from "@/components/SmartCanvasWidget/CanvasTools";
import {Application, Assets, Container, Graphics, Sprite, Texture} from "pixi.js";

/*
 * 别名说明：
 * 画板：drawing-board-container
 * 画板背景：drawing-board-container-background
 * 画板资源容器：drawing-board-assets-container
 * 画板资源扩展容器：image-extend-container
 * 扩展-画笔容器：drawing-painting-container
 * 扩展-选区容器：drawing-district-container
 * */

class CanvasApplication
{
    // 容器
    $container: HTMLElement;

    // app对象
    app: Application;

    // 当前工具
    private static tools: CanvasTools;

    // canvas的几何信息
    private static canvasGeometry: CanvasGeometryInterface = {
        width: 0,
        height: 0,
        scale: 0,
        x: 0,
        y: 0
    };

    // 画板的几何信息
    private static drawingBoardGeometry: DrawingBoardGeometryInterface = {
        width: 0,
        height: 0,
        scale: 0,
        sourceWidth: 0,
        sourceHeight: 0,
        x: 0,
        y: 0
    };

    // 动画帧id
    private static resizeAnimationFrameId: number | null = null;

    // 当前容器尺寸改变回调
    private static resizeFn: Function | null = null;

    // 构造函数
    constructor($container: HTMLElement, options: GeometryInterface)
    {
        const {width = 0, height = 0, scale = 0} = options || {};

        // 容器
        this.$container = $container;

        // 实例化app对象
        this.app = new Application();

        // 实例化工具
        CanvasApplication.tools = new CanvasTools({
            app: this.app
        });

        // 初始化canvas容器几何信息
        this.initCanvasGeometry();

        // 初始化画板几何信息
        this.initDrawingBoardGeometry({width, height, scale});
    }

    /**
     * 初始化
     * @param {OptionsInterface} options 配置项
     * */
    init(options: OptionsInterface): Promise<void>
    {
        return new Promise<void>((resolve) =>
        {
            // 初始化app对象
            this.app.init({
                width: options.width || CanvasApplication.canvasGeometry.width,
                height: options.height || CanvasApplication.canvasGeometry.height,
                backgroundColor: options.backgroundColor || 0xffffff,
                backgroundAlpha: options.backgroundAlpha || 1,
                antialias: options.antialias ?? true,
                autoDensity: options.autoDensity ?? true,
                resolution: options.resolution === "auto" ? window.devicePixelRatio || 1 : isString(options.resolution) ? Number(options.resolution) : isNumber(options.resolution) ? options.resolution : window.devicePixelRatio || 1,
                autoStart: options.autoStart ?? true,
                clearBeforeRender: options.clearBeforeRender || true,
                preserveDrawingBuffer: options.preserveDrawingBuffer || false,
                powerPreference: options.powerPreference || "high-performance",
                resizeTo: options.resizeTo || this.$container
            }).finally(() =>
            {
                // 缓存尺寸改变回调
                CanvasApplication.resizeFn = options.resize || null;

                // 初始化画板
                this.initDrawingBoard();

                // 添加app对象到容器中
                this.$container.appendChild(this.app.canvas);

                // 绑定容器尺寸变化事件
                this.bindContainerResizeEvent();

                resolve();
            });
        });
    }

    /**
     * 获取当前画板几何信息
     * @return {DrawingBoardGeometryInterface} 画板几何信息
     * */
    getDrawingBoardGeometry(): DrawingBoardGeometryInterface
    {
        return CanvasApplication.drawingBoardGeometry;
    }

    /**
     * 设置当前画板缩放
     * @param {number | string} scale 缩放
     * @return {DrawingBoardGeometryInterface} 画板几何信息
     * */
    setDrawingBoardScale(scale: number | string): DrawingBoardGeometryInterface
    {
        // 初始化画板几何信息
        this.initDrawingBoardGeometry({width: CanvasApplication.drawingBoardGeometry.sourceWidth, height: CanvasApplication.drawingBoardGeometry.sourceHeight, scale});

        // 刷新
        this.refresh();

        // 返回画板几何信息
        return CanvasApplication.drawingBoardGeometry;
    }

    /**
     * 设置当前画板尺寸
     * @param {number} width 宽度
     * @param {number} height 高度
     * @return {DrawingBoardGeometryInterface} 画板几何信息
     * */
    setDrawingBoardSize(width: number, height: number): DrawingBoardGeometryInterface
    {
        // 初始化画板几何信息
        this.initDrawingBoardGeometry({width, height, scale: CanvasApplication.drawingBoardGeometry.scale});

        // 刷新
        this.refresh();

        // 返回画板几何信息
        return CanvasApplication.drawingBoardGeometry;
    }

    /**
     * 添加画板图片资源
     * @param {string} alias 别名
     * @param {string} src 图片地址
     * @return {Promise<void>} Promise
     * */
    addImage(alias: string, src: string): Promise<void>
    {
        return new Promise<void>((resolve, reject) =>
        {
            // 添加图片资源
            Assets.add([{alias, src, data: {crossOrigin: "anonymous"}}]);

            // 获取图片资源
            Assets.load([alias]).then((res: Record<string, Texture>) =>
            {
                // 找到画板资源容器
                const currentSourceData = CanvasApplication.tools.getApplicationSourceData(this.app.stage, "drawing-board-assets-container");

                if (currentSourceData)
                {
                    const drawingBoardContainer = currentSourceData as Container<any>;

                    // 创建精灵
                    const sprite = Sprite.from(res[alias]);

                    // 兼容当前图片与容器的尺寸
                    const geometry = CanvasApplication.tools.alignContentToContainer({
                        content: {
                            width: sprite.texture.source.width,
                            height: sprite.texture.source.height
                        },
                        container: {
                            width: CanvasApplication.drawingBoardGeometry.sourceWidth,
                            height: CanvasApplication.drawingBoardGeometry.sourceHeight
                        }
                    });

                    // 设置别名
                    sprite.label = alias;

                    // 设置精灵尺寸
                    sprite.setSize(geometry.width, geometry.height);

                    // 设置精灵位置
                    sprite.position.set(geometry.x, geometry.y);

                    // 创建图片蒙版
                    sprite.setMask({
                        mask: new Graphics().rect(CanvasApplication.drawingBoardGeometry.x, CanvasApplication.drawingBoardGeometry.y, CanvasApplication.drawingBoardGeometry.width, CanvasApplication.drawingBoardGeometry.height).fill(),
                        inverse: false
                    });

                    // 创建图片扩展容器（画笔、快速选区等操作）
                    const imageOperationContainer = new Container();

                    // 设置混合模式
                    imageOperationContainer.blendMode = "normal";

                    // 设置别名
                    imageOperationContainer.label = "image-extend-container";

                    // 添加精灵到画板容器中
                    drawingBoardContainer.addChild(sprite, imageOperationContainer);

                    console.info(CanvasApplication.drawingBoardGeometry);
                }

                resolve();
            }).catch(() =>
            {
                reject();
            });
        });
    }

    /**
     * 刷新
     * */
    refresh(): void
    {
        // 获取画板容器
        const drawingBoardContainer = CanvasApplication.tools.getApplicationSourceData(this.app.stage, "drawing-board-container");

        if (!drawingBoardContainer)
        {
            return;
        }

        // 同步到显示对象
        drawingBoardContainer.scale.set(CanvasApplication.drawingBoardGeometry.scale);
        drawingBoardContainer.position.set(CanvasApplication.drawingBoardGeometry.x, CanvasApplication.drawingBoardGeometry.y);

        // 找到画板背景容器
        const backgroundContainer = CanvasApplication.tools.getApplicationSourceData(this.app.stage, "drawing-board-container-background") as Container<any>;

        // 重新绘制背景网格
        if (backgroundContainer)
        {
            backgroundContainer.removeChildren();

            const sprite = new Sprite(CanvasApplication.tools.createGridTexture({width: CanvasApplication.drawingBoardGeometry.width, height: CanvasApplication.drawingBoardGeometry.height, gap: 8}));

            sprite.scale.set(1 / CanvasApplication.drawingBoardGeometry.scale);

            backgroundContainer.addChild(sprite);
        }

        // 找到当前的背景图容器
        const backgroundImageContainer = CanvasApplication.tools.getApplicationSourceData(this.app.stage, "drawing-board-assets-container") as Container<any>;

        if (backgroundImageContainer && backgroundImageContainer.children && backgroundImageContainer.children.length)
        {
            // 当前的背景图
            const backgroundImage: Sprite = backgroundImageContainer.children[0] as Sprite;

            // 更新主图蒙版信息
            CanvasApplication.tools.refreshImageMaskGeometry(backgroundImage, CanvasApplication.drawingBoardGeometry);
        }

        // 立即重渲染
        this.app.renderer.render(this.app.stage);
    }

    // 初始化画板
    private initDrawingBoard(): void
    {
        // 当前容器
        const container = new Container();

        // 设置别名
        container.label = "drawing-board-container";

        // 居中画板
        container.position.set(CanvasApplication.drawingBoardGeometry.x, CanvasApplication.drawingBoardGeometry.y);

        // 设置画板缩放
        container.scale.set(CanvasApplication.drawingBoardGeometry.scale);

        // 初始化画板背景
        const backgroundContainer = this.initDrawingBoardBackground();

        // 初始化画板资源容器
        const sourceContainer = this.initDrawingBoardSource();

        // 添加画板背景到画板中
        container.addChild(backgroundContainer, sourceContainer);

        // 添加画板到容器中
        this.app.stage.addChild(container);
    }

    // 绑定容器尺寸变化事件
    private bindContainerResizeEvent(): void
    {
        // 创建容器尺寸变化监听器
        const observer = new ResizeObserver((entries: ResizeObserverEntry[]) =>
        {
            // 使用防抖机制合并多次变化
            if (!CanvasApplication.resizeAnimationFrameId)
            {
                CanvasApplication.resizeAnimationFrameId = requestAnimationFrame(() =>
                {
                    for (const entry of entries)
                    {
                        const {width, height} = entry.contentRect || {};

                        if (Math.floor(CanvasApplication.canvasGeometry.width) !== Math.floor(width) || Math.floor(CanvasApplication.canvasGeometry.height) !== Math.floor(height))
                        {
                            // 改变后的尺寸
                            CanvasApplication.canvasGeometry.width = width;
                            CanvasApplication.canvasGeometry.height = height;

                            // 初始化画板几何信息
                            this.initDrawingBoardGeometry({width: CanvasApplication.drawingBoardGeometry.sourceWidth, height: CanvasApplication.drawingBoardGeometry.sourceHeight, scale: "auto"});

                            // 刷新
                            this.refresh();

                            if (CanvasApplication.resizeFn && isFunction(CanvasApplication.resizeFn))
                            {
                                CanvasApplication.resizeFn.apply(this, [CanvasApplication.drawingBoardGeometry]);
                            }
                        }
                    }

                    // 重置状态
                    CanvasApplication.resizeAnimationFrameId = null;
                });
            }
        });

        // 绑定容器尺寸变化事件
        observer.observe(this.$container);
    }

    // 初始化canvas容器几何信息
    private initCanvasGeometry(): void
    {
        // 获取容器的几何信息
        const geometry = this.$container.getBoundingClientRect();

        // 设置canvas容器几何信息
        CanvasApplication.canvasGeometry.width = geometry.width;
        CanvasApplication.canvasGeometry.height = geometry.height;
        CanvasApplication.canvasGeometry.scale = 1;
        CanvasApplication.canvasGeometry.x = geometry.x;
        CanvasApplication.canvasGeometry.y = geometry.y;
    }

    // 初始化画板几何信息
    private initDrawingBoardGeometry(argumentsList: GeometryInterface): void
    {
        const {width = 0, height = 0} = argumentsList || {};

        // 计算画板几何信息
        const geometry = CanvasApplication.tools.alignContentToContainer({
            content: {
                width,
                height
            },
            container: {
                width: CanvasApplication.canvasGeometry.width,
                height: CanvasApplication.canvasGeometry.height
            },
            limit: 0.8,
            scale: argumentsList.scale
        });

        // 设置画板几何信息
        CanvasApplication.drawingBoardGeometry.sourceWidth = width;
        CanvasApplication.drawingBoardGeometry.sourceHeight = height;
        CanvasApplication.drawingBoardGeometry.width = geometry.width;
        CanvasApplication.drawingBoardGeometry.height = geometry.height;
        CanvasApplication.drawingBoardGeometry.scale = geometry.scale;
        CanvasApplication.drawingBoardGeometry.x = geometry.x;
        CanvasApplication.drawingBoardGeometry.y = geometry.y;
    }

    // 初始化画板背景
    private initDrawingBoardBackground(): Container<any>
    {
        // 创建画板背景
        const background = new Container();

        // 设置别名
        background.label = "drawing-board-container-background";

        // 当前背景精灵
        const sprite = new Sprite(CanvasApplication.tools.createGridTexture({width: CanvasApplication.drawingBoardGeometry.width, height: CanvasApplication.drawingBoardGeometry.height, gap: 8}));

        sprite.scale.set(1 / CanvasApplication.drawingBoardGeometry.scale);

        // 添加图形到画板中
        background.addChild(sprite);

        return background;
    }

    // 初始化画板资源容器
    private initDrawingBoardSource(): Container<any>
    {
        // 创建画板资源容器
        const sourceContainer = new Container();

        // 设置别名
        sourceContainer.label = "drawing-board-assets-container";

        return sourceContainer;
    }
}

export default CanvasApplication;
